<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { NAutoComplete, NButton, NInput, useDialog, useMessage } from 'naive-ui'
import html2canvas from 'html2canvas'
import { storeToRefs } from 'pinia'
import { Message } from './components'
import { useScroll } from './hooks/useScroll'
import { useChat } from './hooks/useChat'
import { useCopyCode } from './hooks/useCopyCode'
import { useUsingContext } from './hooks/useUsingContext'
import HeaderComponent from './components/Header/index.vue'
import { HoverButton, SvgIcon } from '@/components/common'
import { useBasicLayout } from '@/hooks/useBasicLayout'
import { useChatStore, usePromptStore } from '@/store'
import { fetchChatAPIProcess } from '@/api'
import { t } from '@/locales'
// 打赏支持
const rewardSupportCard = ref(false)
//   1: 支付宝 2: 微信
const QRCodeModel = ref(1)
let controller = new AbortController()

// const openLongReply = import.meta.env.VITE_GLOB_OPEN_LONG_REPLY === 'true'

const route = useRoute()
const dialog = useDialog()
const ms = useMessage()

const chatStore = useChatStore()

useCopyCode()

const { isMobile } = useBasicLayout()
const { addChat, updateChat, updateChatSome, getChatByUuidAndIndex }
	= useChat()
const { scrollRef, scrollToBottom } = useScroll()
const { usingContext, toggleUsingContext } = useUsingContext()

const { uuid } = route.params as { uuid: string }

const dataSources = computed(() => chatStore.getChatByUuid(+uuid))
const getEnabledNetwork = computed(() => chatStore.getEnabledNetwork)
// const conversationList = computed(() => dataSources.value.filter(item => (!item.inversion && !item.error)))

const prompt = ref<string>('')
const loading = ref<boolean>(false)

// 添加PromptStore
const promptStore = usePromptStore()
// 使用storeToRefs，保证store修改后，联想部分能够重新渲染
const { promptList: promptTemplate } = storeToRefs<any>(promptStore)

function handleSubmit() {
	onConversation()
}

async function onConversation() {
	const message = prompt.value
	if (loading.value)
		return

	if (!message || message.trim() === '')
		return

	controller = new AbortController()

	addChat(+uuid, {
		dateTime: new Date().toLocaleString(),
		text: message,
		inversion: true,
		error: false,
		conversationOptions: null,
		requestOptions: { prompt: message, options: null },
	})
	scrollToBottom()

	loading.value = true
	prompt.value = ''

	const options: Chat.ConversationRequest = {
		conversationId: usingContext.value
			? window.location.hash
			: Math.random().toString(),
	}
	// const lastContext = conversationList.value[conversationList.value.length - 1]?.conversationOptions

	// if (lastContext && usingContext.value)
	//   options = { ...lastContext }

	addChat(+uuid, {
		dateTime: new Date().toLocaleString(),
		text: '',
		loading: true,
		inversion: false,
		error: false,
		conversationOptions: null,
		requestOptions: { prompt: message, options: { ...options } },
	})
	scrollToBottom()

	try {
		await fetchChatAPIProcess<Chat.ConversationResponse>({
			prompt: message,
			options,
			signal: controller.signal,
			network: !!chatStore.getEnabledNetwork,
			onDownloadProgress: ({ event }) => {
				const xhr = event.target
				const { responseText } = xhr
				const chunk = responseText
				try {
					// if (import.meta.env.MODE != 'development') {
						const data = JSON.parse(chunk)
						let texts = ''
						let str = data.result;
						let i = 0;

						setTimeout(function () {
							let se = setInterval(function () {
								i++;
								texts = str.slice(0, i);
								updateChat(+uuid, dataSources.value.length - 1, {
									dateTime: new Date().toLocaleString(),
									text: texts,
									inversion: false,
									error: false,
									loading: false,
									conversationOptions: {},
									requestOptions: { prompt: message, options: { ...options } },
								})


								scrollToBottom()
								if (i == str.length) {
									clearInterval(se);
									texts = str;
								}
							}, 8);
						}, 0);
					// }

					updateChat(+uuid, dataSources.value.length - 1, {
						dateTime: new Date().toLocaleString(),
						text: chunk ?? '',
						inversion: false,
						error: false,
						loading: false,
						conversationOptions: {},
						requestOptions: { prompt: message, options: { ...options } },
					})
					scrollToBottom()
				}
				catch (error) {
					//
				}
			},
		})
	}
	catch (error: any) {
		const errorMessage = error?.text ?? t('common.wrong')

		if (error.text === 'canceled') {
			updateChatSome(+uuid, dataSources.value.length - 1, {
				loading: false,
			})
			scrollToBottom()
			return
		}

		const currentChat = getChatByUuidAndIndex(
			+uuid,
			dataSources.value.length - 1,
		)

		if (currentChat?.text && currentChat.text !== '') {
			updateChatSome(+uuid, dataSources.value.length - 1, {
				text: `${currentChat.text}\n[${errorMessage}]`,
				error: false,
				loading: false,
			})
			return
		}

		updateChat(+uuid, dataSources.value.length - 1, {
			dateTime: new Date().toLocaleString(),
			text: errorMessage,
			inversion: false,
			error: true,
			loading: false,
			conversationOptions: null,
			requestOptions: { prompt: message, options: { ...options } },
		})
		scrollToBottom()
	}
	finally {
		loading.value = false
	}
}

async function onRegenerate(index: number) {
	if (loading.value)
		return

	controller = new AbortController()

	const { requestOptions } = dataSources.value[index]

	const message = requestOptions?.prompt ?? ''

	let options: Chat.ConversationRequest = {}

	if (requestOptions.options)
		options = { ...requestOptions.options }

	loading.value = true

	updateChat(+uuid, index, {
		dateTime: new Date().toLocaleString(),
		text: '',
		inversion: false,
		error: false,
		loading: true,
		conversationOptions: null,
		requestOptions: { prompt: message, ...options },
	})
	try {
		await fetchChatAPIProcess<Chat.ConversationResponse>({
			prompt: message,
			options,
			network: !!chatStore.getEnabledNetwork,
			signal: controller.signal,
			onDownloadProgress: ({ event }) => {
				const xhr = event.target
				const { responseText } = xhr

				// Always process the final line
				// const lastIndex = responseText.lastIndexOf('\n')
				const chunk = responseText
				// if (lastIndex !== -1)
				// chunk = responseText.substring(lastIndex)
				try {
					// const data = JSON.parse(chunk)
					updateChat(+uuid, dataSources.value.length - 1, {
						dateTime: new Date().toLocaleString(),
						text: chunk + 12,
						inversion: false,
						error: false,
						loading: false,
						conversationOptions: {},
						requestOptions: { prompt: message, options: { ...options } },
					})
					scrollToBottom()
				}
				catch (error) {
					//
				}
			},
		})
	}
	catch (error: any) {
		if (error.text === 'canceled') {
			updateChatSome(+uuid, index, {
				loading: false,
			})
			return
		}

		const errorMessage = error?.text ?? t('common.wrong')

		updateChat(+uuid, index, {
			dateTime: new Date().toLocaleString(),
			text: errorMessage,
			inversion: false,
			error: true,
			loading: false,
			conversationOptions: null,
			requestOptions: { prompt: message, ...options },
		})
	}
	finally {
		loading.value = false
	}
}

function handleExport() {
	if (loading.value)
		return

	const d = dialog.warning({
		title: t('chat.exportImage'),
		content: t('chat.exportImageConfirm'),
		positiveText: t('common.yes'),
		negativeText: t('common.no'),
		onPositiveClick: async () => {
			try {
				d.loading = true
				const ele = document.getElementById('image-wrapper')
				const canvas = await html2canvas(ele as HTMLDivElement, {
					useCORS: true,
				})
				const imgUrl = canvas.toDataURL('image/png')
				const tempLink = document.createElement('a')
				tempLink.style.display = 'none'
				tempLink.href = imgUrl
				tempLink.setAttribute('download', 'chat-shot.png')
				if (typeof tempLink.download === 'undefined')
					tempLink.setAttribute('target', '_blank')

				document.body.appendChild(tempLink)
				tempLink.click()
				document.body.removeChild(tempLink)
				window.URL.revokeObjectURL(imgUrl)
				d.loading = false
				ms.success(t('chat.exportSuccess'))
				Promise.resolve()
			}
			catch (error: any) {
				console.error('error', error)
				ms.error(t('chat.exportFailed'))
			}
			finally {
				d.loading = false
			}
		},
	})
}

function handleDelete(index: number) {
	if (loading.value)
		return

	dialog.warning({
		title: t('chat.deleteMessage'),
		content: t('chat.deleteMessageConfirm'),
		positiveText: t('common.yes'),
		negativeText: t('common.no'),
		onPositiveClick: () => {
			chatStore.deleteChatByUuid(+uuid, index)
		},
	})
}

function handleClear() {
	chatStore.toggleNetwork()
}

function handleEnter(event: KeyboardEvent) {
	if (!isMobile.value) {
		if (event.key === 'Enter' && !event.shiftKey) {
			event.preventDefault()
			handleSubmit()
		}
	}
	else {
		if (event.key === 'Enter' && event.ctrlKey) {
			event.preventDefault()
			handleSubmit()
		}
	}
}

function handleStop() {
	if (loading.value) {
		controller.abort()
		loading.value = false
	}
}

// 可优化部分
// 搜索选项计算，这里使用value作为索引项，所以当出现重复value时渲染异常(多项同时出现选中效果)
// 理想状态下其实应该是key作为索引项,但官方的renderOption会出现问题，所以就需要value反renderLabel实现
const searchOptions = computed(() => {
	if (prompt.value.startsWith('/')) {
		return promptTemplate.value
			.filter((item: { key: string }) =>
				item.key.toLowerCase().includes(prompt.value.substring(1).toLowerCase()),
			)
			.map((obj: { value: any }) => {
				return {
					label: obj.value,
					value: obj.value,
				}
			})
	}
	else {
		return []
	}
})
// value反渲染key
const renderOption = (option: { label: string }) => {
	for (const i of promptTemplate.value) {
		if (i.value === option.label)
			return [i.key]
	}

	return []
}

const placeholder = computed(() => {
	if (isMobile.value)
		return t('chat.placeholderMobile')
	return t('chat.placeholder')
})

const buttonDisabled = computed(() => {
	return loading.value || !prompt.value || prompt.value.trim() === ''
})

const footerClass = computed(() => {
	let classes = ['p-4']
	if (isMobile.value) {
		classes = [
			'sticky',
			'left-0',
			'bottom-0',
			'right-0',
			'p-2',
			'pr-3',
			'overflow-hidden',
		]
	}
	return classes
})

onMounted(() => {
	scrollToBottom()
})

onUnmounted(() => {
	if (loading.value)
		controller.abort()
})
</script>

<template>
	<div class="flex flex-col w-full h-full">
		<HeaderComponent v-if="isMobile" :using-context="usingContext" @export="handleExport"
			@toggle-using-context="toggleUsingContext" />
		<main class="flex-1 overflow-hidden">
			<div id="scrollRef" ref="scrollRef" class="h-full overflow-hidden overflow-y-auto">
				<div id="image-wrapper" class="w-full max-w-screen-xl m-auto dark:bg-[#101014]"
					:class="[isMobile ? 'p-2' : 'p-4']">
					<template v-if="!dataSources.length">
						<div class="flex items-center flex-col justify-center mt-4 text-center">
							<SvgIcon icon="ri:bubble-chart-fill" class="mr-2 text-3xl" />
							<p>欢迎来到彩色之外…… 泰裤辣</p>

							<!-- 打赏 Start -->
							<div id="app">
								<span class="reward-btn" style="padding: 5px 12px; cursor: pointer"
									@click="rewardSupportCard = true">🎉</span>
								<!-- 打赏支持 -->
								<div v-show="rewardSupportCard" class="reward-support">
									<div class="reward-support__card">
										<div class="reward-support__head">
											<button type="button" :class="{ 'reward-support__btn--active': QRCodeModel == 1 }"
												class="reward-support__btn" @click="QRCodeModel = 1">
												支付宝
											</button>
											<button type="button" :class="{ 'reward-support__btn--active': QRCodeModel == 2 }"
												:style="{ backgroundColor: QRCodeModel == 2 ? '#21aa38' : '' }" class="reward-support__btn"
												@click="QRCodeModel = 2">
												微信
											</button>
										</div>
										<div class="reward-support__QRCode" style="margin-bottom: 10px">
											<img v-if="QRCodeModel == 1" src="https://zhang-kun8888.gitee.io/reward---project/zhifubao.jpg"
												alt="">
											<img v-else src="https://zhang-kun8888.gitee.io/reward---project/weixin.jpg" alt="">
										</div>
										<div style="margin-bottom: 10px; text-align: center; color: #baccbe">
											<span v-if="QRCodeModel == 1">支付宝扫一扫</span>
											<span v-else>微信扫一扫</span>
										</div>
										<div class="reward-support__quit">
											<span @click="rewardSupportCard = false">下次一定</span>
										</div>
									</div>
									<div class="reward-support__shadow" @click.stop="rewardSupportCard = false" />
								</div>
								<!-- <iframe id="my-iframe" src="https://www.twmm.eu.org/#/" frameborder="0" style="width: 100%; height: 96vh"></iframe> -->
							</div>
							<!-- 打赏 End -->
						</div>
					</template>
					<template v-else>
						<Message v-for="(item, index) of dataSources" :key="index" :date-time="item.dateTime" :text="item.text"
							:inversion="item.inversion" :error="item.error" :loading="item.loading" @regenerate="onRegenerate(index)"
							@delete="handleDelete(index)" />
						<div class="sticky bottom-0 left-0 flex justify-center">
							<NButton v-if="loading" type="warning" @click="handleStop">
								<template #icon>
									<SvgIcon icon="ri:stop-circle-line" />
								</template>
								Stop Responding
							</NButton>
						</div>
					</template>
				</div>
			</div>
		</main>

		<footer :class="footerClass">
			<div class="flex items-center justify-between space-x-2">
				<HoverButton tooltip="点击关闭或开启联网功能，开启后会自动从互联网获得信息来回答您，关闭联网能极大加快响应速度">
					<span class="text-xl text-[#4f555e]" @click="handleClear">
						<!-- <SvgIcon icon="ri:delete-bin-line" /> -->
						<span v-if="getEnabledNetwork" style="color: #2979ff; width: 56px; display: inline-block">
							<img src="https://img2.baidu.com/it/u=723815578,3534923192&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=319"
								alt="联网开启" style="color: #2979ff; width: 56px; display: inline-block">
						</span>
						<span v-if="!getEnabledNetwork" style="color: red; width: 56px; display: inline-block"> <img
								src="https://img2.baidu.com/it/u=147774391,3852711953&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500"
								alt="联网关闭" style="width: 30px;height: 30px; margin: 5px 0 0 15px;border-radius: 50%;"></span>
					</span>
				</HoverButton>
				<NAutoComplete v-model:value="prompt" :options="searchOptions" :render-label="renderOption">
					<template #default="{ handleInput, handleBlur, handleFocus }">
						<NInput v-model:value="prompt" type="textarea" :placeholder="placeholder"
							:autosize="{ minRows: 1, maxRows: 2 }" @input="handleInput" @focus="handleFocus" @blur="handleBlur"
							@keypress="handleEnter" />
					</template>
				</NAutoComplete>
				<HoverButton v-if="!isMobile" @click="handleExport">
					<span class="text-xl text-[#4f555e] dark:text-white">
						<SvgIcon icon="ri:download-2-line" />
					</span>
				</HoverButton>

				<HoverButton v-if="!isMobile" @click="toggleUsingContext">
					<span class="text-xl" :class="{
						'text-[#4b9e5f]': usingContext,
						'text-[#a8071a]': !usingContext,
					}">
						<SvgIcon icon="ri:chat-history-line" />
					</span>
				</HoverButton>
				<NButton type="primary" :disabled="buttonDisabled" @click="handleSubmit">
					<template #icon>
						<span class="dark:text-black">
							<SvgIcon icon="ri:send-plane-fill" />
						</span>
					</template>
				</NButton>
			</div>
		</footer>
	</div>
</template>

<style scoped>
#app {
	position: relative;
	z-index: 9999;
}

.reward-btn {
	position: fixed;
	top: 15%;
	left: 55%;
	transform: translate(-50%, -50%);
	animation: breath_light 2s infinite;
}

@keyframes breath_light {
	0% {
		filter: brightness(100%);
	}

	50% {
		filter: brightness(80%);
		transform: scale(1.1);
	}

	100% {
		filter: brightness(120%);
	}
}

.reward-support {
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
}

.reward-support .reward-support__card {
	position: absolute;
	top: 10%;
	left: 50%;
	transform: translateX(-50%);
	width: 300px;
	padding: 24px;
	border-radius: 12px;
	background-color: #f8f8f8;
	z-index: 1;
}

.reward-support .reward-support__head {
	display: flex;
	justify-content: center;
	margin-bottom: 14px;
}

.reward-support .reward-support__btn {
	width: 65px;
	height: 25px;
	border: 0;
	margin-right: 10px;
	border-radius: 6px;
	font-size: 14px;
	background-color: #ebebeb;
	cursor: pointer;
	color: #8e8e94;
}

.reward-support .reward-support__QRCode {
	width: 163px;
	height: 163px;
	margin: 0 auto;
	background-color: #fff;
}

.reward-support .reward-support__QRCode img {
	width: 100%;
}

.reward-support .reward-support__quit {
	text-align: center;
}

.reward-support .reward-support__quit span {
	width: 120px;
	height: 36px;
	line-height: 36px;
	display: inline-block;
	border-radius: 6px;
	background-color: #ffffff;
	cursor: pointer;
	font-size: 14px;
}

.reward-support .reward-support__quit span:hover {
	background-color: #ebebeb;
}

.reward-support .reward-support__btn--active {
	background-color: #1777ff;
	color: #fff;
}

.reward-support .reward-support__shadow {
	width: 100%;
	height: 100%;
	background-color: #7f7f7f;
	opacity: 0.7;
}
</style>
